use anyhow::{anyhow, Result};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use std::{fmt::Debug, vec};
use std::{
    fs::File,
    io::{BufRead, BufReader},
    ops::Range,
};
use tracing::{debug, error};

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SearchParam {
    pub query: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RegexResult {
    pub line_prefix: String,
    pub line_end: String,
    pub line_no: usize,
    pub mit_word: String,
}

pub fn grep_file(files: &Vec<String>, grep: &str) -> Result<HashMap<String, Vec<RegexResult>>> {
    let mut ret: HashMap<String, Vec<RegexResult>> = HashMap::new();
    if grep.trim().len() == 0 {
        return Ok(ret);
    }

    let vs: Vec<_> = grep.split(" ").collect();
    let regex_s = if vs.len() == 1 {
        let char1 = vs[0].chars().next().unwrap();
        if char1.len_utf8() > 1 {
            debug!("char1.len_utf8() > 1, chinese ? = {}: {}", vs[0], char1);
            format!(".*({}).*", vs[0])
        } else {
            if char1.is_ascii_uppercase() {
                format!(".*({}|{}).*", vs[0], de_capitalize_first_letter(&vs[0]))
            }else{
                format!(".*({}|{}).*", vs[0], capitalize_first_letter(&vs[0]))
            }     
        }   
    } else {
        if vs.len() > 2 {
            // error!("too more words, lost some words: {}", vs[2..].join(" "));
        }
        format!(".*{}(.*){}.*|.*{}(.*){}.*", vs[0], vs[1], vs[1], vs[0])        
    };
    debug!("regex: {}", regex_s);

    let regex = Regex::new(regex_s.as_str()).map_err(|_| anyhow!("error to regex"))?;
    // let regex = Regex::new(grep).map_err(|_| anyhow!("error to regex"))?;
    // let escapes = &vec!["target".to_string()];
    for file in files {
        let one = if vs.len() > 1 {
            file_regex(file, &regex, &vs[0], &vs[1])?
        } else {
            file_regex(file, &regex, &vs[0],&vs[0])?
        };
        if one.len() > 0 {
            ret.insert(file.to_string(), one);
        }
    }

    if ret.is_empty() {
        let m = if vs.len() > 1 {
            "regex.captures"
        } else {
            "regex.find"
        };
        debug!("regex search: {} with {} not found!", grep, m)
    }
    return Ok(ret);
}

/**
目录遍历，目录检索，目录文件读取

读取特定目录下符合目标ftypes的所有文件，并放弃指定的特殊目录escapes。

*/
pub fn path_walk(path: &Path, escapes: &Vec<String>, ftypes: &Vec<String>) -> Result<Vec<String>> {
    //let escapes = vec!["target","pkg","__pycache__"];
    //let ftypes = vec!["txt","rs","md","go"];, if ftypes.len()==0, then all file is ok
    // debug!(?path, ?escapes,? ftypes);
    let mut rets: Vec<String> = vec![];
    for entry in path.read_dir()? {
        if let Ok(entry) = entry {
            if let Ok(ft) = entry.file_type() {
                // .unwrap().is_dir(){
                let ename = entry
                    .file_name()
                    .as_os_str()
                    .to_str()
                    .ok_or(anyhow!("e"))?
                    .to_string();
                if ft.is_dir() {
                    // println!("inner -> {:#?}", ename);
                    if !escapes.contains(&ename) && !ename.starts_with(".") {
                        let mut subf = path_walk(&entry.path().as_path(), escapes, ftypes)?;
                        rets.append(&mut subf);
                    } else {
                        // println!("break {:#?}", ename );
                    }
                } else if ft.is_file() {
                    let ext = entry
                        .path()
                        .extension()
                        .unwrap_or_default()
                        .to_str()
                        .unwrap_or_default()
                        .to_lowercase();
                    // debug!("path = {:#?}, file_name={:#?}, ext = {}", entry.path(), entry.file_name(), ext);
                    if !ename.starts_with(".") && !ename.is_empty() {
                        if ftypes.len() == 0 || ftypes.contains(&ext) {
                            rets.push(entry.path().display().to_string());
                        }
                    }
                }
            }
        }
    }
    Ok(rets)
}

pub fn collect_files(todo_dir: &str, ftype: &str) -> Option<Vec<String>> {
    let mypath = Path::new(todo_dir);
    let p = mypath.parent();
    // debug!("parent path is  {:?}", p);

    let escapes = vec![
        "target".to_string(),
        "pkg".to_string(),
        "__pycache__".to_string(),
    ];

    // let ftype = "txt, rs, js, py, md, go";
    let ftypes: Vec<String> = ftype.split(",").map(|s| s.trim().to_string()).collect();

    match path_walk(mypath, &escapes, &ftypes) {
        Ok(files) => {
            if files.len() > 0 {
                Some(files)
            } else {
                None
            }
        }
        Err(err) => {
            error!(?err);
            None
        }
    }
}

/// 对文件进行诸行regex查找，并返回命中的行结果。
/* 
pub fn file_regex(filename: &str, regex: &Regex) -> Result<Vec<RegexResult>> {
    let f = File::open(&filename).map_err(|_| anyhow!("e"))?;
    let reader = BufReader::new(f); 
    let mut mit: Vec<_> = reader
        .lines()
        .enumerate()
        .map(|(lineno, line)| {
            line.ok()
                .map(|line| {
                    regex
                        .find(&line)                        
                        .map(|m| format_line(filename, &line, lineno + 1, m.range()))
                })
                .flatten()
        })
        .filter_map(|v| v.ok_or(()).ok())
        .collect();

    regex.find(filename).map(|m| {
        let fm = format_line(filename, filename, 0, m.range());
        mit.push(fm);
    });

    Ok(mit)
}
*/

fn  regex_captures_to_result(regex:&Regex, line:&str, lineno:usize, wa:&str, wb:&str) -> Option<RegexResult> {

    if let Some(caps) = regex.captures(line) {
        if caps.len() == 2{
            let s_result = if let Some(m) = caps.get(1) {
                let start = m.start();
                let end = m.end();
                Some(RegexResult {
                    line_prefix: (&line[0..start]).to_owned(),
                    line_end: (&line[end..]).trim_end().to_owned(),
                    mit_word: (&line[start..end]).to_owned(),
                    line_no: lineno,
                })
            }else{
                None
            };
            return s_result;
        }else if caps.len() == 3 {

            let s_result = if let Some(m) = caps.get(1) {
                let start = m.start() - wa.len();
                let end = m.end() + wb.len();
                Some(RegexResult {
                    line_prefix: (&line[0..start]).to_owned(),
                    line_end: (&line[end..]).trim_end().to_owned(),
                    mit_word: (&line[start..end]).to_owned(),
                    line_no: lineno,
                })
            } else if let Some(m) = caps.get(2) {
                let start = m.start() - wb.len();
                let end = m.end() + wa.len();
                Some(RegexResult {
                    line_prefix: (&line[0..start]).to_owned(),
                    line_end: (&line[end..]).trim_end().to_owned(),
                    mit_word: (&line[start..end]).to_owned(),
                    line_no: lineno,
                })
            } else {
                None
            };
            return s_result;
        }else{
            return None;
        }
    }
    
    return None;    
}


pub fn file_regex(filename: &str, regex: &Regex, wa: &str, wb: &str) -> Result<Vec<RegexResult>> {
    let f = File::open(&filename).map_err(|_| anyhow!("e"))?;
    let reader = BufReader::new(f);
    // let mut lines = String::new();
    let mut mit: Vec<_> = reader
        .lines()
        .enumerate()
        .map(|(lineno, line)| {
            line.ok()
                .map(|line| {                   
                   regex_captures_to_result(regex, &line, lineno, wa, wb)                   
                })
                .flatten()
        })
        .filter_map(|v| v.ok_or(()).ok())
        .collect();

    if let Some(fm) = regex_captures_to_result(regex, filename, 0, wa, wb){
            mit.push(fm);
    }
    Ok(mit)
}

fn capitalize_first_letter(s: &str) -> String {
    s[0..1].to_uppercase() + &s[1..]
}
fn de_capitalize_first_letter(s: &str) -> String {
    s[0..1].to_lowercase() + &s[1..]
}
/// 格式化输出匹配的行，包含行号、列号和带有高亮的第一个匹配项
pub fn format_line(filename: &str, line: &str, lineno: usize, range: Range<usize>) -> RegexResult {
    let Range { start, end } = range;
    let prefix = &line[..start];
    //let color_word = format!("{}", &line[start..end].green().bold())
    /*
    let line_code = format!(
        "{}<MARK>{}</MARK>{}",
        prefix.trim_start(),
        &line[start..end],
        &line[end..].trim_end()
    );
    */
    return RegexResult {
        line_prefix: prefix.trim_start().to_owned(),
        line_end: (&line[end..]).trim_end().to_owned(),
        mit_word: (&line[start..end]).to_owned(),
        line_no: lineno,
    };
}
